/*************************************************
* Title：TinyToolKit
* Author：Opportunity 
* Discribe：实现对象池的主要逻辑
* CreateTime：2020-04-05 21:33:30
* Version：1.0
* Modify Recoder：
*************************************************/

using System;
using System.Collections.Generic;

namespace TinyToolKit.ObjectPool
{
    public class TObjectPool<T> : IReference, IObjectPool<T> where T :class
    {
        #region Variable
        private List<ObjectPoolItem<T>> poolItemList;//存储所有生成的对象
        private Dictionary<T, ObjectPoolItem<T>> poolItemMap;//存储场景中正在使用的对象
        private int curIndex;//指向poolItemList中未使用的靠前排列的item对象
        private Func<int,T> spawnFunc; //实例化对象的方法
        private Action<T> destroyFunc;//释放对象池中对象时调用的方法

        private int oriCapacity;
        private int spawnIndex;
        private float releaseTimer;
        private bool isAutoRelease; 
        private float autoReleaseInterval; 

        public string Name { get; set; }
        public int Capacity => poolItemList.Count;
        public int usingCount => poolItemMap.Count;
        public int unUsedCount => Capacity - usingCount;
        public bool IsAutoRelease => isAutoRelease;
        public float AutoReleaseTime => autoReleaseInterval;
        public float CurReleaseTimer => releaseTimer;

        #endregion

        #region Object Pool
        public TObjectPool(string poolName,int capacity, Func<int, T> spawnFunc, Action<T> destroyFunc = null)
        {
            InitPool(poolName, capacity, spawnFunc, destroyFunc);
        }

        /// <summary>
        /// 产生对象
        /// </summary>
        public T Spawn()
        {
            ObjectPoolItem<T> poolItemTemp = null;

            lock (poolItemList)
            {
                for (int i = 0; i < poolItemList.Count; i++)
                {
                    if (poolItemList[curIndex].IsUsing)
                    {
                        curIndex++;
                        if (curIndex >= poolItemList.Count) curIndex = 0;
                    }
                    else
                    {
                        poolItemTemp = poolItemList[curIndex];
                        break;
                    }
                }

                if (poolItemTemp == null)
                {
                    poolItemTemp = CreateObjectPoolItem();
                    curIndex = 0;
                }
            }

            poolItemTemp.Comsume();
            poolItemMap.Add(poolItemTemp.Target, poolItemTemp);

            return poolItemTemp.Target;
        }

        /// <summary>
        /// 回收对象
        /// </summary>
        /// <param name="obj">需要回收的对象</param>
        public bool Recycle(T obj)
        {
            if (poolItemMap.ContainsKey(obj))
            {
                ObjectPoolItem<T> poolItem = poolItemMap[obj];
                poolItem.Release();
                poolItemMap.Remove(obj);
                return true;
            }

            TLogTool.Error("{0}不是由{1}对象池生成的对象！", obj.GetType().Name ,typeof(T).Name);
            return false;
        }

        /// <summary>
        /// 释放对象
        /// </summary>
        /// <param name="obj">需要释放的对象</param>
        public bool Recycle(object obj)
        {
            if (obj is T)
            {
                return Recycle(obj as T);
            }

            TLogTool.Error("{0}不属于{1}对象池的对象类型！", obj.GetType().Name, typeof(T).Name);
            return false;
        }

        /// <summary>
        /// 优先释放未被使用的对象，如果释放数量大于剩余未使用的数量，则释放所有未使用的
        /// </summary>
        public void Release(int releaseCount)
        {
            if (releaseCount < 0) return;

            int realReleaseCount = releaseCount > unUsedCount ? unUsedCount : releaseCount;
             
            int tempIndex = 0; 
            ObjectPoolItem<T>[] tempArray = new ObjectPoolItem<T>[realReleaseCount]; 
            lock (poolItemList)
            {
                for (int i = 0; i < poolItemList.Count; i++)
                {
                    if (poolItemList[i].IsUsing == false)
                    {
                        tempArray[tempIndex] = poolItemList[i];
                        tempIndex++;
                        if (tempIndex >= realReleaseCount)
                            break;
                    }
                }

                for (int i = 0; i < tempArray.Length; i++)
                {
                    ReleasePoolItem(tempArray[i]);
                }
            }
        }

        /// <summary>
        /// 释放所有未被使用的对象
        /// </summary>
        public void ReleaseAllUnused()
        {
            Release(unUsedCount);
        }

        /// <summary>
        /// 间隔一定时间，自动释放未使用的对象
        /// </summary>
        /// <param name="time">自动释放的间隔时间</param>
        public void ActiceAutoRelease(float time)
        {
            isAutoRelease = true;
            releaseTimer = 0;
            autoReleaseInterval = time;
        }

        /// <summary>
        /// 取消自动释放
        /// </summary>
        public void DeacticeAutoRelease()
        {
            isAutoRelease = false;
        }

        public void Update(float deltaTime)
        {
            if (isAutoRelease)
            {
                releaseTimer += deltaTime;
                if (releaseTimer > autoReleaseInterval)
                {
                    releaseTimer = 0;
                    if (Capacity > oriCapacity)
                        Release(Capacity - oriCapacity);
                }
            }
        }

        public void ShutDown()
        {
            Clear();
        }

        public void Clear()
        {
            poolItemList.Clear();
            poolItemMap.Clear();
            curIndex = 0;
            spawnFunc = null;
            destroyFunc = null;
            oriCapacity = 0;
            spawnIndex = 0;
            releaseTimer = 0;
            isAutoRelease = false;
            autoReleaseInterval = 0;
        }
        #endregion

        #region Internal Func
        /// <summary>
        /// 初始化对象池中一定数量的对象
        /// </summary>
        private void InitPool(string poolName, int capacity, Func<int, T> factoryFunc, Action<T> destroyFunc = null)
        {
            poolItemList = new List<ObjectPoolItem<T>>();
            poolItemMap = new Dictionary<T, ObjectPoolItem<T>>();

            Name = poolName;
            spawnFunc = factoryFunc;
            this.destroyFunc = destroyFunc;
            oriCapacity = capacity;
            isAutoRelease = false;
            autoReleaseInterval = float.MaxValue;
            for (int i = 0; i < capacity; i++)
            {
                CreateObjectPoolItem();
            }
        }

        //创建ObjectPoolItem
        private ObjectPoolItem<T> CreateObjectPoolItem()
        {
            ObjectPoolItem<T> poolItem = TReferencePool.Acquire<ObjectPoolItem<T>>();
            poolItem.Target = spawnFunc(spawnIndex);
            poolItemList.Add(poolItem);

            spawnIndex++;
            if (spawnIndex >= int.MaxValue)
                spawnIndex = 0;

            return poolItem;
        }

        //释放ObjectPoolItem
        private void ReleasePoolItem(ObjectPoolItem<T> item)
        {
            curIndex = 0;
            destroyFunc?.Invoke(item.Target);
            poolItemList.Remove(item);
            TReferencePool.Release(item);
        }
        #endregion
    }
}
